﻿/**************************************************************************
创建时间:	2020/6/28
作	  者:	张存
邮 	  箱:	zhangcunliang@126.com

Copyright (c) zhcun.cn

描	述：
记	录：
***************************************************************************/
using ASmile.DBModel;
using ASmile.Extend;
using ASmile.Helpers;
using ASmile.ORM;
using ASmile.ORM.BuildSQLText;
using ASmile.ORM.Cores;
using ASmile.ORM.Entitys;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ASmile.Com.Da
{
    public delegate void BeforeExec(Type entityType, ILoginSession loginSession, ISqlBuilder sqlBuilder, EmDbOperation opType);

    public delegate void AfterExec(EntityBase model, EmDbOperation operation, ExecResult result);

    public class DBMain : DBContextDebug
    {
        #region 静态构造函数

        static DBMain()
        {
            //EnumHelper.StringToEnum(CommonConfig.THIS.CoreDbType, out _DbType);
            DBType = GetDbType(ConfigCom.M.CoreDbType);
            _ConnStr = ConfigCom.M.CoreDbConnStr;
            RunToDBTask();
            if (ConfigCom.M.LogSqlIgnoreTable.IsNotEmpty())
            {
                LogSqlIgnoreTable = ConfigCom.M.LogSqlIgnoreTable.SplitByNotEmpty(",");
            }
            DBCacheEvent.Init();
        }

        readonly static string _ConnStr;

        /// <summary>
        /// 返回数据库类型
        /// </summary>
        public static EmDbType DBType { get; private set; }

        static string[] LogSqlIgnoreTable { set; get; }

        /// <summary>
        /// 是否保存sql日志
        /// </summary>
        static bool LogSqlSave { get { return ConfigCom.M.LogSqlSave; } }

        #endregion

        public DBMain()
            : this(_ConnStr)
        { }

        public DBMain(string connStr)
            : this(connStr, DBType)
        { }

        public DBMain(string connStr, EmDbType dbType)
            : base(dbType, connStr)
        { }

        public DBMain(string connStr, string dbType)
            : base(dbType, connStr)
        { }

        public ILoginSession LoginSession { set; get; }

        //public static IExecEvent ExecEvent { set; get; }

        public static event BeforeExec OnBeforeExec;

        public static event AfterExec OnAfterExec;

        public static event Action<BaseResult> OnResultFinish;
        /// <summary>
        /// 当执行增删改之后触发的事件
        /// </summary>
        //public event Action<EntityBase, EmDbOperation, ExecResult> OnExecAfter;

        void ExecAction(EntityBase model, EmDbOperation operation, ExecResult result)
        {
            if (OnAfterExec == null)
            {
                return;
            }
            OnAfterExec(model, operation, result);
        }

        const string FN_ADD_USER_ID = "AddUserId";
        const string FN_ADD_HOST = "AddHost";
        const string FN_ADD_TIME = "AddTime";
        const string FN_LAST_USER_ID = "LastUserId";
        const string FN_LAST_HOST = "LastHost";
        const string FN_LAST_TIME = "LastTime";
        const string P_PARAM_HOST = "Arg_LoginHost";
        const string P_PARAM_USER_ID = "Arg_LoginUserId";

        protected override void ResultFinish(BaseResult result)
        {
            //TODO: 这里可以记录sql执行日志
            //Console.WriteLine(result.SqlContent);
            OnResultFinish?.Invoke(result);

            if (!LogSqlSave) return;

            if (DbHelper.ConnStr.StringEquals(ConfigCom.M.LogDbConnStr)) return; //不保存日志表的日志

            if (LogSqlIgnoreTable != null && LogSqlIgnoreTable.Contains(result.TableName))
            {
                //不保存已配置的表
                return;
            }

            AddLog(new Log_Sql
            {
                Interval = (decimal)result.ExecInterval,
                LogTime = DateTime.Now,
                TableName = result.TableName,
                ResultMark = result.ResultMark,
                RowCount = result.RowCount,
                SqlContent = result.SqlContent,
                AddHost = LoginSession?.LoginHost,
                AddUser = LoginSession?.CurrUser?.UserName
            });
        }

        #region 记录日志

        static Queue<Log_Sql> LogQueues { get; } = new Queue<Log_Sql>();

        static AutoResetEvent LogTaskEvent { get; } = new AutoResetEvent(false);

        void AddLog(Log_Sql model)
        {
            if (!ConfigCom.M.LogSqlSave) return;

            LogQueues.Enqueue(model);
            LogTaskEvent.Set();
        }

        static void RunToDBTask()
        {
            if (!LogSqlSave) return;

            Task.Factory.StartNew(ToDBTask);
            void ToDBTask()
            {
                var log = LogHelper.CreateLog("LogSqlHttp");

                var db = new DBContextDebug(DBType, ConfigCom.M.LogDbConnStr);

                //DBMain db = new DBMain(CommonConfig.THIS.LogDbConnStr);
                log.Info("开始sql日志保存任务");
                while (LogSqlSave)
                {
                    try
                    {
                        if (LogQueues.Count > 0)
                        {
                            var m = LogQueues.Dequeue();
                            if (m == null) continue;
                            db.Insert(m);
                            continue;
                        }
                    }
                    catch (Exception ex)
                    {
                        log.Error("保存sql日志出错", ex);
                    }
                    LogTaskEvent.WaitOne(TimeSpan.FromMinutes(1));
                }
            }
        }

        #endregion

        public ExecResult Insert<TEntity>(TEntity entity, bool autoId, bool autoUserId)
            where TEntity : EntityBase, new()
        {
            if (autoId)
            {
                var id = ReflectionHelper.GetPropertyValue<string>(entity, "Id");
                if (id.IsEmpty())
                {
                    id = GuidHelper.NewId();
                    ReflectionHelper.SetPropertyValue(entity, "Id", id);
                }
            }
            if (autoUserId)
            {
                ReflectionHelper.SetPropertyValue(entity, FN_ADD_USER_ID, LoginSession?.CurrUser?.UserId);
            }
            ReflectionHelper.SetPropertyValue(entity, FN_ADD_HOST, LoginSession?.LoginHost);

            //保证AddTime由数据库自动赋值,程序赋值对于AddTime 无效
            entity.ClearChangedState(FN_ADD_TIME, FN_LAST_TIME, FN_LAST_HOST, FN_LAST_USER_ID);

            return base.Insert(entity);
        }

        public override ExecResult Insert<TEntity>(TEntity entity)
        {
            var r = Insert(entity, true, true);

            ExecAction(entity, EmDbOperation.Create, r);
            return r;
        }

        public void SetDefaultVal<TEntity>(TEntity entity) where TEntity : EntityBase, new()
        {
            if (LoginSession?.CurrUser?.UserId != null)
            {
                ReflectionHelper.SetPropertyValue(entity, FN_ADD_USER_ID, LoginSession?.CurrUser?.UserId);
            }
            ReflectionHelper.SetPropertyValue(entity, FN_ADD_TIME, DateTime.Now);
            ReflectionHelper.SetPropertyValue(entity, FN_ADD_HOST, LoginSession?.LoginHost);
            ReflectionHelper.SetPropertyValue(entity, FN_LAST_HOST, string.Empty);
            ReflectionHelper.SetPropertyValue(entity, FN_LAST_TIME, DateTime.Now);
            ReflectionHelper.SetPropertyValue(entity, FN_LAST_USER_ID, string.Empty);
        }

        public override int InsertBulk<TEntity>(List<TEntity> dataList)
        {
            //foreach (var entity in dataList)
            //{
            //    SetDefaultVal(entity);
            //}
            //return base.InsertBulk(dataList);
            //2020.2.26 使用 InserBulk 会较长时间，不用了
            if (dataList == null || dataList.Count == 0) return 0;
            try
            {
                foreach (var item in dataList)
                {
                    Insert(item);
                }
                return dataList.Count;
            }
            catch (Exception ex)
            {
                LogHelper.LogObj.Error("批量增加时出错", ex);
                throw ex;
            }
        }
        /// <summary>
        /// 批量新增数据，指定参数是否设置缺省值 用户Id，时间，Hose 等
        /// </summary>
        public int InsertBulk<TEntity>(List<TEntity> dataList, bool setDefaultVale)
             where TEntity : EntityBase, new()
        {
            if (setDefaultVale)
            {
                foreach (var entity in dataList)
                {
                    SetDefaultVal(entity);
                }
            }
            return base.InsertBulk(dataList);
        }

        protected override ExecResult UpdateBase<TEntity>(TEntity entity, Func<ISqlBuilder, string> whereFun)
        {
            ReflectionHelper.SetPropertyValue(entity, FN_LAST_HOST, LoginSession?.LoginHost);
            ReflectionHelper.SetPropertyValue(entity, FN_LAST_USER_ID, LoginSession?.CurrUser?.UserId);
            ReflectionHelper.SetPropertyValue(entity, FN_LAST_TIME, DateTime.Now);
            var cFileds = entity.GetChangedFields();
            var r = base.UpdateBase(entity, whereFun);
            entity.SetFieldChanged(cFileds); //重新设置改变，用于更新缓存判断
            ExecAction(entity, EmDbOperation.Update, r);
            entity.ClearChangedState();
            return r;
        }

        public override ExecResult Delete<TEntity>(TEntity entity)
        {
            var r = base.Delete(entity);
            ExecAction(entity, EmDbOperation.Delete, r);
            return r;
        }

        public override ProcResult ExecProcedure<ProcEntity>(ProcEntity procObj)
        {
            ReflectionHelper.SetPropertyValue(procObj, P_PARAM_HOST, LoginSession?.LoginHost);
            ReflectionHelper.SetPropertyValue(procObj, P_PARAM_USER_ID, LoginSession?.CurrUser?.UserId);
            return base.ExecProcedure(procObj);
        }

        protected override void BeforeExecSqlBuilder<TEntity>(ISqlBuilder sqlBuilder, EmDbOperation opType)
        {
            OnBeforeExec?.Invoke(typeof(TEntity), LoginSession, sqlBuilder, opType);
        }
    }
}